﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;



namespace XoLib.Data
{
    /// <summary>
    /// Number Of Digit
    /// <example>  </example>/// 
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public sealed class Number_Of_Digit : Attribute
    {
        /// <summary>
        /// <para>
        /// <param name="nb">ushort</param>
        /// </para>
        /// Constructor
        /// </summary>
        public Number_Of_Digit(ushort nb)
        {
            this.Nb = nb;
        }

        /// <summary>
        /// Number of digit
        /// <example>  </example>/// 
        /// <returns>ushort</returns>
        /// </summary>
        public ushort Nb { get; private set; }
    }


    /// <summary>
    /// Class for convert IpAddress
    /// <example> IPAddress.LongToString(); IPAddress.StringToLong("192.168.1.1"); </example>/// 
    /// </summary>
    public abstract class KeyCode
    {

        /// <summary>
        /// Constructor
        /// </summary>
        public KeyCode()
        {
            var attributes = this.GetType().GetCustomAttributes(typeof(Number_Of_Digit), false);
            if (attributes.Length == 1)
            {
                NbDigit = ((Number_Of_Digit)attributes[0]).Nb;
            }

            if ((NbDigit < 1) || (NbDigit > int.MaxValue.ToString().Length))
                throw new Exception("Wrong number of digit");
        }


        /// <summary>
        /// <para>
        /// <param name="Num">int</param>
        /// <param name="LC">char</param>
        /// </para>
        /// Constructor
        /// </summary>
        public KeyCode(int Num, char LC)
            : this()
        {
            this.Num = Num;
            this.LC = char.ToUpper(LC);
        }

        /// <summary>
        /// <para>
        /// <param name="Num">int</param>
        /// </para>
        /// Constructor
        /// </summary>
        /*internal*/ protected KeyCode(int Num)
            : this()
        {
            this.Num = Num;
            this.LC = ComputKey(); 
        }

        /// <summary>
        /// <para>
        /// <param name="code">string</param>
        /// </para>
        /// Constructor
        /// </summary>
        public KeyCode(string code)
            : this()
        {
            vache(code);
        }

        /// <summary>
        /// <para>
        /// <param name="code">string</param>
        /// </para>
        /// Constructor
        /// <returns>KeyCode</returns>
        /// </summary>
        public KeyCode vache(string code)
        {
            if (Regex.IsMatch(code, @"^\d{" + Convert.ToString(NbDigit) + "}[A-Za-z+]$"))
            {
                this.Num = Convert.ToInt32(code.Substring(0, NbDigit));
                this.LC = code.Substring(NbDigit, 1).ToUpper().ToCharArray()[0];
                if(this.LC == '+')
                    this.LC = ComputKey();
            }
            else
            {
                this.Num = -1;
                this.LC = ' ';
            }

            return this;
        }


        /// <summary>
        /// Convert to string
        /// <returns>string</returns>
        /// </summary>
#if !WindowsCE
        public string ToString(bool Without = false)
#else
          public string ToString(bool Without)
#endif
        {
            string result = string.Format("{" + string.Format("0,{0}:D{0}", Convert.ToString(NbDigit)) + "}", this.Num);
            return Without ? result : result + char.ToUpper(LC).ToString();
        }


        /// <summary>
        /// Convert to string
        /// <returns>string</returns>
        /// </summary>
        public override string ToString()
        {
            return string.Format("{" + string.Format("0,{0}:D{0}", Convert.ToString(NbDigit)) + "}", this.Num) + char.ToUpper(LC).ToString();
        }

        /// <summary>
        /// Check if the key in valid
        /// <returns>bool</returns>
        /// </summary>    
        public bool IsGoodFormat
        {
            get
            {
                return (this.Num != -1);
            }
        }



        /// <summary>
        /// Check if the key in valid
        /// <returns>bool</returns>
        /// </summary>    
        public bool IsCodeValid
        {
            get
            {
                char CalKey = ComputKey();
                return (CalKey == LC);
            }
        }

        /// <summary>
        /// Comput the key
        /// <returns>KeyCode</returns>
        /// </summary>
        private  char ComputKey()
        {
            int itmp = (this.Num % 23);
            int ical = itmp;
            if (itmp > 7) { ical++; };
            if (itmp > 12) { ical++; };
            if (itmp > 18) { ical++; };
            return ((char)(((int)'A') + (ical)));
        }


        /// <summary>
        /// Number
        /// <returns>int</returns>
        /// </summary>
        public int Num { get; private set; }

        /// <summary>
        /// The key
        /// <returns>char</returns>
        /// </summary>
        public char LC { get; private set; }

        /// <summary>
        /// Numbre of Digit
        /// <returns>ushort</returns>
        /// </summary>
        private ushort NbDigit { get;  set; }

        /// <summary>
        /// Clear Number and Key
        /// </summary>
        public void Clear()
        {
            this.Num = -1;
            this.LC = ' ';
        }
    }
}
